/**
 * @file customsplitter.h
 * @brief
 *
 * 
 *
 * @author 1823419256@qq.com
 * @version 1.0
 * @date 2011
*/

#ifndef MYBOOK_CTRLS_CUSTOMSPLITTER_H_
#define MYBOOK_CTRLS_CUSTOMSPLITTER_H_

#include "..\aspectfactory.h"

#define WM_SPLITTER_NOTIFY (WM_USER + 501)
#define WM_SPLITTER_DRAG (WM_USER + 502)
#define WM_SPLITTER_DROP (WM_USER + 503)

class CCustomSplitter : public CWindowImpl<CCustomSplitter, CWindow>
{
public:
	DECLARE_WND_CLASS(_T("MyCustomSplitter"))
	typedef CWindowImpl<CCustomSplitter, CWindow> baseClass;

	CCustomSplitter(CAspectAbstractFactory* pAspectFactory)
		: m_pAspectFactory(pAspectFactory)
	{
		ATLASSERT(m_pAspectFactory);
	}

	HWND Create(HWND hWndParent, RECT& rect, int nID = 0, DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP, LPCTSTR szWindowName = NULL)
	{
		dwStyle |= WS_CLIPSIBLINGS;
		return baseClass::Create(hWndParent, rect, szWindowName, dwStyle, 0, nID);
	}

	int GetSpliterWidth() const { return 5; }

public:
	BEGIN_MSG_MAP(CCustomSplitter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
		MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
		MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
	END_MSG_MAP()

	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		m_hHandCursor = LoadCursor(NULL, IDC_HAND);
		m_hSizeWECursor = LoadCursor(NULL, IDC_SIZEWE);	

		return 0L;
	}

	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		return 0;
	}

	LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		return TRUE;
	}

	LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CPaintDC dc(m_hWnd);

		CRect rcClient, rcArrow;

		GetClientRect(rcClient);

		HBITMAP hBitmap = CreateCompatibleBitmap(dc, rcClient.Width(), rcClient.Height());
		CDC dcMem;
		dcMem.CreateCompatibleDC(dc);
		dcMem.SelectBitmap(hBitmap);		

		rcClient.InflateRect(0, 0, -1, -1);
		DrawSplitter(dcMem, rcClient, m_pAspectFactory->GetTextColor());

		GetArrowRect(rcArrow, rcClient);
		DrawArrow(dcMem, rcArrow, m_pAspectFactory->GetTextColor(), TRUE);

		rcClient.InflateRect(0, 0, 1, 1);
		dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), dcMem, 0, 0, SRCCOPY);		

		DeleteObject(hBitmap);

		return 0L;
	}

	LRESULT OnSetCursor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		HWND hWnd = (HWND)wParam;
		UINT nHitTest = (UINT)LOWORD(lParam);
		UINT message = (UINT)HIWORD(lParam);

		CPoint pt;
		GetCursorPos(&pt);
		ScreenToClient(&pt);

		CRect rcArrow;
		GetArrowRect(rcArrow);
		rcArrow.InflateRect(1, 1);

		if (rcArrow.PtInRect(pt))
			SetCursor(m_hHandCursor);
		else
			SetCursor(m_hSizeWECursor);

		return TRUE;
	}

	LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		CPoint ptClient = point;
		ClientToScreen(&point);

		CRect rcArrow;
		GetArrowRect(rcArrow);
		rcArrow.InflateRect(1, 1);

		if (rcArrow.PtInRect(ptClient))
		{
			::SendMessage(GetParent(), WM_SPLITTER_NOTIFY, 0, 0);
		}
		else
		{
			m_nPrePos = point.x;
			SetCapture();

			::SendMessage(GetParent(), WM_SPLITTER_DRAG, 0, 0);
		}

		return 0L;
	}

	LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		ClientToScreen(&point);

		if (GetCapture() == m_hWnd)
		{
			if (point.x != m_nPrePos)
			{
				int dx = point.x - m_nPrePos;
				m_nPrePos = point.x;
				lParam = dx >= 0 ? MAKELPARAM(1, dx) : MAKELPARAM(0, -dx);
				::SendMessage(GetParent(), WM_SPLITTER_NOTIFY, 1, lParam);
			}

			::SendMessage(GetParent(), WM_SPLITTER_DRAG, 0, 0);

			ReleaseCapture();
		}

		return 0L;
	}

	LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CPoint point(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		ClientToScreen(&point);

		if (GetCapture() == m_hWnd)
		{
			if (point.x != m_nPrePos)
			{
				int dx = point.x - m_nPrePos;
				m_nPrePos = point.x;
				lParam = dx >= 0 ? MAKELPARAM(1, dx) : MAKELPARAM(0, -dx);
				::SendMessage(GetParent(), WM_SPLITTER_NOTIFY, 1, lParam);
			}
		}

		return 0L;
	}

public:
	void SetArrowDirect(BOOL bLeft)
	{
		if (m_bLeftArrow && bLeft)
			return;

		if (!m_bLeftArrow && !bLeft)
			return;

		m_bLeftArrow = bLeft;

		if (IsWindow())
			InvalidateRect(NULL, TRUE);
	}

protected:
	void GetArrowRect(LPRECT lpRectArrow, LPRECT lpRectClient = NULL)
	{
		CRect rcClient;

		if (lpRectClient != NULL)
			rcClient = *lpRectClient;
		else
		{
			GetClientRect(rcClient);
			rcClient.InflateRect(0, 0, -1, -1);
		}

		lpRectArrow->left = rcClient.left + 1;
		lpRectArrow->right = rcClient.right - 1;

		int nHalf = (rcClient.top + rcClient.bottom) / 2;

		lpRectArrow->top = nHalf - 2;
		lpRectArrow->bottom = nHalf + 2;
	}

	void DrawSplitter(CDC& dc, CRect& rc, COLORREF clr)
	{
		CPen pen;
		pen.CreatePen(PS_SOLID, 1, clr);
		HPEN hOldPen = dc.SelectPen(pen);

		dc.MoveTo(rc.TopLeft());
		dc.LineTo(rc.left, rc.bottom);
		dc.LineTo(rc.BottomRight());
		dc.LineTo(rc.right, rc.top);
		dc.LineTo(rc.TopLeft());

		BYTE r = GetRValue(clr) / 5;
		BYTE g = GetGValue(clr) / 5;
		BYTE b = GetBValue(clr) / 5;

		COLORREF clrLine[3];
		clrLine[0] = RGB(r * 2, g * 2, b * 2);
		clrLine[1] = RGB(r * 3, g * 3, b * 3);
		clrLine[2] = RGB(r * 4, g * 4, b * 4);

		POINT pt1 = { rc.left + 1, rc.top + 1 };
		POINT pt2 = { rc.left + 1, rc.bottom};

		for (int i = 0; i < 3; ++i)
		{
			CPen pen1;
			pen1.CreatePen(PS_SOLID, 1, clrLine[i]);
			HPEN hOldPen1 = dc.SelectPen(pen1);

			dc.MoveTo(pt1);
			dc.LineTo(pt2);
			pt1.x++;
			pt2.x++;

			dc.SelectPen(hOldPen1);
			pen1.DeleteObject();
		}

		dc.SelectPen(hOldPen);
	}

	void DrawArrow(CDC& dc, CRect& rc, COLORREF clr, BOOL bLeft)
	{
		CPen pen;
		pen.CreatePen(PS_SOLID, 1, clr);
		HPEN hOldPen = dc.SelectPen(pen);

		if (m_bLeftArrow)
		{
			int x = rc.left;
			int y = (rc.top + rc.bottom) / 2;
			for (int i = 0; i < 3; ++i)
			{
				if (i == 0)
				{
					dc.MoveTo(x, y);
					dc.LineTo(x, y + 1);
				}
				else if (i == 1)
				{
					dc.MoveTo(x + 1, y - 1);
					dc.LineTo(x + 1, y + 2);
				}
				else if (i == 2)
				{
					dc.MoveTo(x + 2, y - 2);
					dc.LineTo(x + 2, y + 3);
				}
			}
		}
		else
		{
			int x = rc.right;
			int y = (rc.top + rc.bottom) / 2;
			for (int i = 0; i < 3; ++i)
			{
				if (i == 0)
				{
					dc.MoveTo(x, y);
					dc.LineTo(x, y + 1);
				}
				else if (i == 1)
				{
					dc.MoveTo(x - 1, y - 1);
					dc.LineTo(x - 1, y + 2);
				}
				else if (i == 2)
				{
					dc.MoveTo(x - 2, y - 2);
					dc.LineTo(x - 2, y + 3);
				}
			}
		}

		dc.SelectPen(hOldPen);
	}

protected:
	HCURSOR m_hHandCursor;
	HCURSOR m_hSizeWECursor;
	BOOL m_bLeftArrow;
	int m_nPrePos;

protected:
	boost::scoped_ptr<CAspectAbstractFactory> m_pAspectFactory;
};

#endif // MYBOOK_CTRLS_CUSTOMSPLITTER_H_